﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SQLToolbox.Types;
namespace SQLToolbox.Controller.Tools
{
    public class DBConnector
    {
        private Dictionary<string, DataSource> dataSources = new Dictionary<string, DataSource>();
        public DBConnector()
        {
            DataSource ds = new DataSource
            {
                Name = "MS SQL 2005",
                Locator = new SQLToolbox.DAL.SMOServerLocator(),
                SchemaProvider = new SQLToolbox.DAL.SMOServerSchemaProvider(),
                ScriptExecuter = new SQLToolbox.DAL.SqlClientScriptExecuter(),
                SchemaObjectsMatcher = new SQLToolbox.DAL.SMOObjectsMatcher()

            };
            dataSources.Add(ds.Name, ds);
        }
        public IEnumerable<string> AvailableDataSources
        {
            get
            {
                return dataSources.Keys;
            }
        }

        public IEnumerable<string> GetServers(string dataSourceName)
        {
            if (!dataSources.ContainsKey(dataSourceName))
                return null;//throw new ControllerException("Data Source doesn't exist");
            return dataSources[dataSourceName].Locator.ServersList;
        }

        public bool TryToConnect(DBConnectionProperties conProp)
        {

            try
            {
                Connect(conProp);
                //if (string.IsNullOrEmpty(conProp.DatabaseName))
                //{	
                //    string s = dataSources[conProp.DataSource].SchemaProvider.ServerVersion;
                //}
                //else
                return dataSources[conProp.DataSource].SchemaProvider.Databases.Contains(conProp.DatabaseName);

            }
            catch (SQLTooboxDALException)
            {
                return false;
            }
        }

        public IEnumerable<string> GetDatabases(DBConnectionProperties conProp)
        {
            try
            {
                Connect(conProp);
                return dataSources[conProp.DataSource].SchemaProvider.Databases;
            }
            catch (SQLTooboxDALException e)
            {
                throw new SQLToolboxControllerException(e.Message, e);
            }
        }

        public Schema GetSchema(DBConnectionProperties conProp)
        {
            try
            {
                Connect(conProp);
            }
            catch (SQLToolboxControllerException)
            {
                return null;
            }
            var e = new EventHandler<DatabaseObjectProcessingEventArgs>(SchemaProvider_DatabaseObjectProcessing);
            dataSources[conProp.DataSource].SchemaProvider.DatabaseObjectProcessing += e;
            var s = dataSources[conProp.DataSource].SchemaProvider.GetSchema(conProp.DatabaseName); ;
            dataSources[conProp.DataSource].SchemaProvider.DatabaseObjectProcessing -= e;
            return s;
        }

        void SchemaProvider_DatabaseObjectProcessing(object sender, DatabaseObjectProcessingEventArgs e)
        {
            FireDatabaseEventOccuredEvent(string.Format("Processing {0}: {1}", e.ObjectType, e.ObjectName));
        }

        public Schema GetSchema(DBConnectionProperties conProp, IDictionary<SchemaObjectType, IEnumerable<string>> requiredProperties)
        {
            try
            {
                Connect(conProp);
            }
            catch (SQLToolboxControllerException)
            {
                return null;
            }
            var e = new EventHandler<DatabaseObjectProcessingEventArgs>(SchemaProvider_DatabaseObjectProcessing);
            dataSources[conProp.DataSource].SchemaProvider.DatabaseObjectProcessing += e;
            var s = dataSources[conProp.DataSource].SchemaProvider.GetSchema(conProp.DatabaseName, requiredProperties); ;
            dataSources[conProp.DataSource].SchemaProvider.DatabaseObjectProcessing -= e;
            return s;
        }



        public SchemaObject GetSchemaObject(DBConnectionProperties conProp, SchemaObjectType type, string objectName)
        {
            try
            {
                Connect(conProp);
            }
            catch (SQLToolboxControllerException)
            {
                return null;
            }
            return dataSources[conProp.DataSource].SchemaProvider.GetObject(conProp.DatabaseName, type, objectName);
        }


        public void ExecuteScript(DBConnectionProperties connectionProperties, Script script)
        {
            try
            {
                dataSources[connectionProperties.DataSource].ScriptExecuter.ExecuteScriptNonQuery(
                    connectionProperties, script);
            }
            catch (SQLTooboxDALException e)
            {
                throw new SQLToolboxControllerException(e.Message, e);
            }
        }


        public Script CreateAlterScript(DBConnectionProperties sourceConnectionProperties,
        DBConnectionProperties targetConnectionProperties, SQLToolbox.Types.SchemaObjectBase obj)
        {
            ISchemaObjectsMatcher m = dataSources[sourceConnectionProperties.DataSource].SchemaObjectsMatcher;
            m.InitSourceDatabase(sourceConnectionProperties);
            m.InitTargetDatabase(targetConnectionProperties);
            m.CreateAlterScript(obj);
            return m.FinishBatch();
        }

        public Script CreateDropScript(DBConnectionProperties connectionProperties, SQLToolbox.Types.SchemaObjectBase obj)
        {
            ISchemaObjectsMatcher m = dataSources[connectionProperties.DataSource].SchemaObjectsMatcher;
            m.InitTargetDatabase(connectionProperties);
            m.CreateDropScript(obj);
            return m.FinishBatch();
        }

        /// <summary>
        /// Creates the match script.
        /// </summary>
        /// <param name="sourceConnectionProperties">The source connection properties.</param>
        /// <param name="targetConnectionProperties">The target connection properties.</param>
        /// <param name="objsActions">A list of object to match each object is represented by a tuple of a 
        /// SchemaObjectBase and a boolean. If the boolean is true - then the action to be performed is an Alter
        /// otherwise Drop</param>
        /// <returns></returns>
        public Script CreateMatchScript(DBConnectionProperties sourceConnectionProperties,
                                                     DBConnectionProperties targetConnectionProperties, IEnumerable<SQLToolbox.Lib.Tuple<SQLToolbox.Types.SchemaObjectBase,bool>> objsActions)
        {
            ISchemaObjectsMatcher m = dataSources[sourceConnectionProperties.DataSource].SchemaObjectsMatcher;
            m.InitSourceDatabase(sourceConnectionProperties);
            m.InitTargetDatabase(targetConnectionProperties);
            foreach (var t in objsActions)
            {
                if (t.b)
                    m.CreateAlterScript(t.a);
                else
                    m.CreateDropScript(t.a);
            }
            return m.FinishBatch();
        }


        public event EventHandler<DBConnectorEventArgs> DatabaseEventOccured;
        private void FireDatabaseEventOccuredEvent(string message)
        {
            if (DatabaseEventOccured != null)
            {
                DatabaseEventOccured(this, new DBConnectorEventArgs
                {
                    Message = message
                });
            }
        }

        private void Connect(DBConnectionProperties conProp)
        {
            if (!dataSources.ContainsKey(conProp.DataSource))
                throw new SQLToolboxControllerException("Data Source doesn't exist");
            dataSources[conProp.DataSource].SchemaProvider.SetConnection(conProp);

        }
    }


    public class DBConnectorEventArgs : EventArgs
    {
        public string Message { get; set; }
    }

    [global::System.Serializable]
    public class SQLToolboxControllerException : Exception
    {
        public SQLToolboxControllerException() { }
        public SQLToolboxControllerException(string message) : base(message) { }
        public SQLToolboxControllerException(string message, Exception inner) : base(message, inner) { }
        protected SQLToolboxControllerException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }
}
